{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GGnS0Q-mILcy"
   },
   "source": [
    "# Chapter 6 - Defining Functions：函式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "g0va3iQRRS6i"
   },
   "source": [
    "關鍵字 `def` 就是定義 Definition 的意思，透過這個方式可以建立一個函式，並接收傳入的參數，來定義我們想要讓程式執行的動作。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ccLxH1qAkMym"
   },
   "source": [
    "References:\n",
    "\n",
    "* [Defining Functions - Python Documentation](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "P9kdLlryIM1_"
   },
   "source": [
    "### 基本用法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TL2ewBM-xdMj"
   },
   "source": [
    "建立一個函式時，可以為這個函式指定一個名字，接下來以四個空格的縮排開始一段新的表達式："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "executionInfo": {
     "elapsed": 1413,
     "status": "ok",
     "timestamp": 1607673174090,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "sb2PJyaCIWGT"
   },
   "outputs": [],
   "source": [
    "def say_hello():           # 初始化一個叫做 say_hello 的函式，且不需要傳入任何參數\n",
    "    print(\"Hello world!\")  # 這個韓式之內，只有一個調用 print() 函式的動作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1235,
     "status": "ok",
     "timestamp": 1607673201423,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "4IjEhoJPIcG8",
    "outputId": "76011286-57df-47e8-9d5a-e07e7bf0eea7"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello world!\n"
     ]
    }
   ],
   "source": [
    "say_hello()  # 執行 say_hello() 函式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 586,
     "status": "ok",
     "timestamp": 1607673281517,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "5vlywCFxIdH_",
    "outputId": "6068077a-fe7a-46c4-f873-bc52f579e6d8"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello world!\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "hello = say_hello()  # 嘗試將 say_hello() 函式執行的結果指定給 hello 物件\n",
    "print(hello)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YMHqboPtyuyf"
   },
   "source": [
    "函式執行完，依情況可以用 `return` 關鍵字，來指定要回傳的物件："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "executionInfo": {
     "elapsed": 948,
     "status": "ok",
     "timestamp": 1607673106601,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "iuiY0rkwIGsx"
   },
   "outputs": [],
   "source": [
    "def func():      # 初始化一個叫做 func 的函式，且不需要傳入任何參數\n",
    "    return None  # 這個函式回傳了一個 None 的物件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "executionInfo": {
     "elapsed": 888,
     "status": "ok",
     "timestamp": 1607673415452,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "a9WRK5hVIUcX"
   },
   "outputs": [],
   "source": [
    "func()  # 執行 func() 函式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1311,
     "status": "ok",
     "timestamp": 1607673478577,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "hVDY8pdKzZvO",
    "outputId": "85c1d363-b954-4a41-a201-0e6de2719663"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n"
     ]
    }
   ],
   "source": [
    "reaction = func()  # 嘗試將 func() 函式執行的結果指定給 reaction 物件\n",
    "print(reaction)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "executionInfo": {
     "elapsed": 1506,
     "status": "ok",
     "timestamp": 1607673605166,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "EvZrktXhIiiv"
   },
   "outputs": [],
   "source": [
    "def give_me_five():      # 初始化一個叫做 give_me_five 的函式\n",
    "    print(\"High five!\")  # 讓函式調用 print() 函式，顯示 \"High five!\" 的字串\n",
    "    return 5             # 讓函式回傳整數 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1377,
     "status": "ok",
     "timestamp": 1607673607647,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "etylie00z_gb",
    "outputId": "e535476b-f242-40b2-d41c-e35b9e12e5e2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "High five!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "give_me_five()  # 執行 give_me_five() 函式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1303,
     "status": "ok",
     "timestamp": 1607673633639,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "8Qcfj3AkIrTu",
    "outputId": "ca0fa829-2200-47d7-98b9-7c7e04a22d0c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "High five!\n",
      "5\n",
      "<class 'int'>\n"
     ]
    }
   ],
   "source": [
    "answer = give_me_five()  # 嘗試將 give_me_five() 函式執行的結果指定給 answer 物件\n",
    "print(answer)\n",
    "print(type(answer))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0rr7uVmCIvX7"
   },
   "source": [
    "### 添加傳入參數 (Arguments / Parameters)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "oGDTD8tA0VS4"
   },
   "source": [
    "函式可以接收參數，不同參數之間以逗點分隔："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "tEt4d96QI0Yi"
   },
   "outputs": [],
   "source": [
    "def add_them(x, y):  # 新增名為 add_them 的函式，並接收兩個參數：x, y\n",
    "    print(\"x + y = \", x+y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 630,
     "status": "ok",
     "timestamp": 1606974497409,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "OeYLomY1JEyj",
    "outputId": "bbd65d65-84de-4a6c-d781-124ccc3a115c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x + y =  3\n",
      "x + y =  7\n"
     ]
    }
   ],
   "source": [
    "add_them(1, 2)      # 調用函式並對參數 x, y 分別帶入 1, 2\n",
    "add_them(x=3, y=4)  # 調用函式並對參數 x, y 分別帶入 3, 4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "executionInfo": {
     "elapsed": 935,
     "status": "ok",
     "timestamp": 1607674079743,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "4wpx_xZlJK1U"
   },
   "outputs": [],
   "source": [
    "def add_these(x, y, z):  # 這次接收三個參數\n",
    "    print(\"x + y + z = \", x+y+z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1215,
     "status": "ok",
     "timestamp": 1607674085065,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "cvM6ElfYJZ18",
    "outputId": "777861a9-be3b-447b-cc10-73710b06a281"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x + y + z =  6\n"
     ]
    }
   ],
   "source": [
    "add_these(1, 2, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "i8R9j4Iq19bp"
   },
   "source": [
    "調用參數時，若不指定要傳入參數的關鍵字，將會依照建立函式時的設定，依序將接收的物件帶入給各個參數。\n",
    "\n",
    "若要將特定的物件指定給特定的參數，是以一種名為關鍵字參數 (Keyword arguments) 的方法執行，格式是`關鍵字參數名=數值` (`kwargs=value`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1384,
     "status": "ok",
     "timestamp": 1607675314350,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "WFmO04lR6lPT",
    "outputId": "b55ffc4f-0a5e-48db-80cf-6f71aa4ebd4a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x + y + z =  6\n"
     ]
    }
   ],
   "source": [
    "add_these(x=1, y=2, z=3)  # 指定所有的關鍵字參數"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VLAD-gaY6B3R"
   },
   "source": [
    "指定關鍵字參數時，參數順序可以調換。也可以僅指定部分的關鍵字參數，但有一個原則：只要指定了一個關鍵字參數，接下來傳入的參數也都要指定為關鍵字參數。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1760,
     "status": "ok",
     "timestamp": 1607675401601,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "ei8jR9Q_JmAk",
    "outputId": "f240e56a-f1f0-40de-8026-49a914a9655d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x + y + z =  6\n",
      "x + y + z =  6\n",
      "x + y + z =  6\n"
     ]
    }
   ],
   "source": [
    "add_these(1, 2, z=3)      # 僅指定一個關鍵字參數\n",
    "add_these(1, y=2, z=3)    # 指定兩個關鍵字參數\n",
    "add_these(1, z=2, y=3)    # 關鍵字參數的順序可以調換"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "executionInfo": {
     "elapsed": 1378,
     "status": "ok",
     "timestamp": 1607675399462,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "NkrfWm6wJ3VM"
   },
   "outputs": [],
   "source": [
    "# add_these(x=1, 2, 3)    # 錯誤：指定了第一個參數為關鍵字參數，但其後的參數沒有指定為關鍵字參數\n",
    "# add_these(x=1, y=2, 3)  # 錯誤：指定了第二個參數為關鍵字參數，但其後的參數沒有指定為關鍵字參數"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "58umcF_a6_XW"
   },
   "source": [
    "參數可以有預設值。當使用者沒有指定傳給有預設值的參數時，就以預設值作為參數的內容。\n",
    "\n",
    "與關鍵字參數相似的概念是：如果指定了一個參數的預設值，則其後的參數也都需要被指定預設值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "executionInfo": {
     "elapsed": 1105,
     "status": "ok",
     "timestamp": 1607675505396,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "GdVg4j-iKAiY"
   },
   "outputs": [],
   "source": [
    "def add_these(x, y, z=0):  # 預先指定關鍵字 z 的傳入值為 0\n",
    "    print(\"x + y + z =\", x+y+z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 989,
     "status": "ok",
     "timestamp": 1607675536584,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "yrFvd_kabZwq",
    "outputId": "0dabf274-af74-4afd-c9cc-e80b713a58e9"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x + y + z = 3\n"
     ]
    }
   ],
   "source": [
    "add_these(1, 2)  # 僅代入 x, y 參數，z 則維持預設值 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "executionInfo": {
     "elapsed": 1463,
     "status": "ok",
     "timestamp": 1607675575049,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "pJfAUjxSKRNV"
   },
   "outputs": [],
   "source": [
    "# def add_these(x=0, y, z):  # 錯誤：指定了第一個參數的預設值，其後的參數也都需要指定預設值。\n",
    "#     print(\"x + y + z = \", x+y+z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "p9yeOpJn77o9"
   },
   "source": [
    "References:\n",
    "\n",
    "* [More on Defining Functions](https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "UKlo-m88KV_G"
   },
   "source": [
    "#### `*args`：可變長度參數 (Arbitrary number of arguments)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "azb8JOui-ctP"
   },
   "source": [
    "在包含多個物件的名稱之前加上 `*` 關鍵字，可以一次將多個物件給依序傳入函式中："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "executionInfo": {
     "elapsed": 1454,
     "status": "ok",
     "timestamp": 1607675943775,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "9pGPvNAjaY_s"
   },
   "outputs": [],
   "source": [
    "def print_them(x, y, z):  # 初始化一個可以接收 x, y, z 三個參數的函式\n",
    "    print(\"x =\", x)\n",
    "    print(\"y =\", y)\n",
    "    print(\"z =\", z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 958,
     "status": "ok",
     "timestamp": 1607676484703,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "8l7saQFOal20",
    "outputId": "fd7b3dd1-db87-460b-fca7-310de42b21ea"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x = 1\n",
      "y = 2\n",
      "z = 3\n"
     ]
    }
   ],
   "source": [
    "numbers = [1, 2, 3]\n",
    "print_them(*numbers)         # 一次將 numbers[0], numbers[1], numbers[2] 給依序傳入函式內\n",
    "# print_them(x=1, y=2, z=3)  # 上一行的用法與此行等價"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Wly-oQKlaT1n"
   },
   "source": [
    "#### `**kwargs`：可變長度關鍵字參數 (Arbitrary number of keyword arguments)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fzr-G7NF_HoM"
   },
   "source": [
    "若在字典物件之前加上 `**` 關鍵字，可以將字典的鍵值對當作關鍵字參數給傳入函式："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "executionInfo": {
     "elapsed": 902,
     "status": "ok",
     "timestamp": 1607676530554,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "56_ONxO_aYB1"
   },
   "outputs": [],
   "source": [
    "def print_them(x, y, z):  # 初始化一個可以接收 x, y, z 三個參數的函式\n",
    "    print(\"x =\", x)\n",
    "    print(\"y =\", y)\n",
    "    print(\"z =\", z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1016,
     "status": "ok",
     "timestamp": 1607676737399,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "hMS-lNisa2Si",
    "outputId": "e24c295f-86a8-41b7-c595-62d2b8ee7e93"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x = 1\n",
      "y = 2\n",
      "z = 3\n"
     ]
    }
   ],
   "source": [
    "numbers_dict = {\n",
    "    \"x\": 1,\n",
    "    \"y\": 2,\n",
    "    \"z\": 3,\n",
    "}\n",
    "print_them(**numbers_dict)  # 將 1 代入參數 x、2 代入參數 y、3 代入參數 z\n",
    "# print_them(x=1, y=2, z=3) # 上一行的用法與此行等價"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "nbUpIXWGbDhO"
   },
   "source": [
    "## 全域"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vFg7kv7KAYHr"
   },
   "source": [
    "在 Python 中，呼叫一個物件名稱時會參照到哪個物件，必須要參考這個物件的作用域 (Scope)，有以下的區別：\n",
    "\n",
    "* Python 原生內建的物件，稱作內建域 (Built-in)\n",
    "* 在沒有建立函式時，建立的物件是生效在全域 (Global)\n",
    "* 若在函式裡建立巢狀（Nested，意指多層次）函式，最底層函式的作用域叫做本地域 (Local)\n",
    "* 巢狀函式中，本地域與全域之間的作用域，稱作封閉域 (Enclosing)\n",
    "\n",
    "所以調用物件時，查詢的順序為：\n",
    "\n",
    "1. 內建域 (Built-in)\n",
    "2. 本地域 (Local)\n",
    "3. 封閉域 (Enclosing)\n",
    "4. 全域 (Global)\n",
    "\n",
    "\n",
    "若要在函式裡呼叫物件，需考慮到物件會調用到哪個區域的物件，才不會出現意料之外的錯誤。\n",
    "\n",
    "如果要強制調用不同作用域的物件，有幾個關鍵字可以使用：\n",
    "\n",
    "1. `global`：在此本地域 (Local) 調用全域 (Global) 物件\n",
    "2. `nonlocal`：在此本地域 (Local) 調用本地域 (Local) 以及全域 (Glocal) 以外的所有物件\n",
    "\n",
    "References:\n",
    "\n",
    "* [Python Scopes and Namespaces](https://docs.python.org/3/tutorial/classes.html#python-scopes-and-namespaces)\n",
    "\n",
    "> 備註：對初學者來說，這真的稍嫌複雜，而且連筆者都沒辦法好好地寫好一個更淺顯的範例⋯⋯於是拿了 Python 官方文件的內容來嘗試跟大家講解。\n",
    "> \n",
    "> 為了讓各位方便理解，筆者將程式碼的執行順序以 [1], [2], ... 標示，等一下請依照標示來閱讀程式內容："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "executionInfo": {
     "elapsed": 1096,
     "status": "ok",
     "timestamp": 1607680395985,
     "user": {
      "displayName": "黃種平",
      "photoUrl": "https://lh3.googleusercontent.com/a-/AOh14GjCoYcYDAMAK-J-MvClxwWJY0fvKYsny6BL962ZNA=s64",
      "userId": "12486487678081777487"
     },
     "user_tz": -480
    },
    "id": "L4bvdokaceQ6",
    "outputId": "1f5b6ab2-8519-4d42-ce3c-ea7ceb6cd090"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2] Global assignment: spam\n",
      "[7] Local assignment: local spam\n",
      "[8] After local assignment: test spam\n",
      "[12] After nonlocal assignment: nonlocal spam\n",
      "[16] After global assignment: nonlocal spam\n",
      "[17] In global scope: global spam\n"
     ]
    }
   ],
   "source": [
    "spam = \"spam\"                                       # [1]\n",
    "print(\"[2] Global assignment:\", spam)               # [2]\n",
    "\n",
    "def scope_test():\n",
    "    def do_local():\n",
    "        spam = \"local spam\"                         # [6]\n",
    "        print(\"[7] Local assignment:\", spam)        # [7]\n",
    "\n",
    "    def do_nonlocal():\n",
    "        nonlocal spam                               # [10]\n",
    "        spam = \"nonlocal spam\"                      # [11]\n",
    "\n",
    "    def do_global():\n",
    "        global spam                                 # [14]\n",
    "        spam = \"global spam\"                        # [15]\n",
    "\n",
    "    spam = \"test spam\"                              # [4]\n",
    "\n",
    "    do_local()                                      # [5]\n",
    "    print(\"[8] After local assignment:\", spam)      # [8]\n",
    "    \n",
    "    do_nonlocal()                                   # [9]\n",
    "    print(\"[12] After nonlocal assignment:\", spam)  # [12]\n",
    "    \n",
    "    do_global()                                     # [13]\n",
    "    print(\"[16] After global assignment:\", spam)    # [16]\n",
    "\n",
    "scope_test()                                        # [3]\n",
    "print(\"[17] In global scope:\", spam)                # [17]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "uYcatCs-F3s7"
   },
   "source": [
    "1. 指定 `\"spam\"` 字串內容給全域 (Global) 的 `spam` 物件\n",
    "2. 顯示 `spam` 物件，內容為全域 (Global) 的 `\"spam\"`\n",
    "3. 呼叫 `scope_test()` 函式\n",
    "4. 建立 `spam` 物件並指定 `\"test spam\"` 字串內容\n",
    "5. 呼叫 `do_local()` 函式\n",
    "6. 指定 `\"local spam\"` 字串內容給本地域 (Local) 的 `spam` 物件\n",
    "7. 顯示 `spam` 物件，內容為本地域 (Local) 的 `\"local spam\"`\n",
    "8. 顯示 `spam` 物件，內容為封閉域 (Enclosing) 的 `\"test spam\"`\n",
    "9. 呼叫 `do_nonlocal()` 函式\n",
    "10. 用 `nonlocal` 關鍵字，改變物件的作用域為封閉域 (Enclosing)\n",
    "11. 指定 \"nonlocal spam\"` 字串內容給封閉域 (Enclosing) 的 `spam` 物件\n",
    "12. 顯示 `spam` 物件，內容為 `nonlocal` 關鍵字宣告後，最接近的 `\"nonlocal spam\"`\n",
    "13. 呼叫 `do_global()` 函式\n",
    "14. 用 `global` 關鍵字，改變物件的作用域為全域 (Global)\n",
    "15. 指定 \"global spam\"` 字串內容給全域 (Global) 的 `spam` 物件\n",
    "16. 顯示 `spam` 物件，內容為 `nonlocal` 關鍵字宣告後，最接近的 `\"nonlocal spam\"`\n",
    "17. 顯示 `spam` 物件，內容為 `global` 關鍵字宣告後，最接近的 `\"global spam\"`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Q8CnDEabQLF3"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "authorship_tag": "ABX9TyNvQPiFP4hSZkUd9EhrQIfH",
   "collapsed_sections": [],
   "name": "Python Basics - Chapter 6 - Functions.ipynb",
   "provenance": [],
   "toc_visible": true
  },
  "jupytext": {
   "formats": "ipynb,py:light"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  },
  "toc-autonumbering": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
